<template>
  <view>
    <!-- #ifdef APP-NVUE -->
    <view @click="open"
      ><view :event-penetration-enabled="true"><slot name="trigger"></slot></view
    ></view>
    <!-- #endif -->
    <!-- #ifndef APP-NVUE -->
    <view @click="open"><slot name="trigger"></slot></view>
    <!-- #endif -->

    <tm-overlay
      ref="overlayAni"
      :in-content="props.inContent"
      :duration="props.duration + 80"
      :z-index="props.zIndex"
      :transprent="!props.mask"
      :align="align_rp"
      v-model:show="_show"
      :teleport="props.teleport"
      :overlay-click="false"
      @open="OverLayOpen"
      @close="overclose"
      @click="overlayClickFun"
    >
      <tm-translate
        ref="drawerANI"
        :reverse="reverse_rp"
        :width="anwidth"
        :height="anheight"
        :auto-play="false"
        :name="aniname"
        :duration="props.duration + 80"
        @end="playEndEvent"
      >
        <view
          :style="[
            { width: anwidth, height: anheight },
            !props.transprent ? tmcomputed.borderCss : '',
            !props.transprent ? tmcomputed.backgroundColorCss : '',
            !props.transprent ? tmcomputed.shadowColor : '',
            customCSSStyle,
          ]"
          :class="[round_rp, 'flex flex-col overflow ', customClass]"
          @click.stop="$event.stopPropagation()"
        >
          <view
            v-if="!props.closeable && !props.hideHeader"
            class="flex flex-row flex-row-center-center flex-between px-24"
            style="height: 44px"
          >
            <view class="flex-4 flex-shrink">
              <tm-text
                v-if="!props.hideCancel && !loading && !ok_loading"
                :label="props.cancelText"
                @click="cancel"
              ></tm-text>
            </view>
            <view class="flex-8 px-32 flex-center">
              <slot name="title"><tm-text _class="text-overflow-1 opacity-7" :label="props.title"></tm-text></slot>
            </view>
            <view class="flex-4 flex-shrink flex-row flex-row-center-end">
              <tm-text
                v-if="!ok_loading && !loading"
                :color="okColor"
                :dark="props.dark"
                :label="props.okText"
                @click="ok"
              ></tm-text>
              <tm-icon
                v-if="ok_loading || loading"
                :color="okColor"
                :spin="ok_loading || loading"
                :dark="isDark"
                :_class="isDark !== true ? 'opacity-4' : ''"
                :font-size="24"
                :name="ok_loading || loading ? 'tmicon-shuaxin' : 'tmicon-times-circle-fill'"
              ></tm-icon>
            </view>
          </view>
          <view
            v-if="props.closeable && !props.hideHeader"
            class="flex flex-row flex-row-center-center flex-between px-24"
            style="height: 44px"
          >
            <view class="flex-9 pr-32">
              <slot name="title"
                ><tm-text _class="text-overflow-1 opacity-7" :dark="props.dark" :label="props.title"></tm-text
              ></slot>
            </view>
            <view class="flex-3 flex-shrink flex-row flex-row-center-end">
              <tm-icon
                :dark="props.dark"
                :_class="isDark !== true ? 'opacity-3' : ''"
                :font-size="36"
                name="tmicon-times-circle-fill"
                @click="cancel"
              ></tm-icon>
            </view>
          </view>
          <!-- #ifdef APP-NVUE -->
          <scroll-view :scroll-y="!props.disabbleScroll" :style="[{ height: contentHeight }]" class="overflow"
            ><slot name="default"></slot
          ></scroll-view>
          <!-- #endif -->
          <!-- #ifndef APP-NVUE -->
          <view
            :style="{
              overflowY: props.disabbleScroll ? 'normal' : 'auto',
              height: contentHeight,
            }"
            ><slot name="default"></slot
          ></view>
          <!-- #endif -->
          <slot name="foot"><view v-if="props.footHeight > 0" class="flex"></view></slot>
        </view>
      </tm-translate>
    </tm-overlay>
  </view>
</template>

<script lang="ts" setup>
/**
 * 抽屉
 * @description 别名poup弹层，提供，左，右，上，下，中弹出内容。
 */
import tmTranslate from '../tm-translate/tm-translate.vue'
import tmText from '../tm-text/tm-text.vue'
import tmIcon from '../tm-icon/tm-icon.vue'
import tmOverlay from '../tm-overlay/tm-overlay.vue'
import { getCurrentInstance, computed, ref, provide, inject, onMounted, onUnmounted, nextTick, watch } from 'vue'
import { cssstyle, tmVuetify, colorThemeType } from '../../tool/lib/interface'
import { custom_props, computedTheme, computedClass, computedStyle, computedDark } from '../../tool/lib/minxs'
import { useTmpiniaStore } from '../../tool/lib/tmpinia'
import { useWindowInfo } from '../../tool/useFun/useWindowInfo'

const drawerANI = ref<InstanceType<typeof tmTranslate> | null>(null)
const overlayAni = ref<InstanceType<typeof tmOverlay> | null>(null)

const store = useTmpiniaStore()
const props = defineProps({
  ...custom_props,
  //是否显示遮罩
  mask: {
    type: [Boolean, String],
    default: true,
  },
  //抽屉放置的位置
  placement: {
    type: String,
    default: 'bottom', //top|left|right|bottom|center
  },
  show: {
    type: [Boolean],
    default: false,
  },
  width: {
    type: Number,
    default: 500,
  },
  height: {
    type: Number,
    default: 600,
  },
  round: {
    type: Number,
    default: 10,
  },
  //弹出的动画时间单位ms.
  duration: {
    type: Number,
    default: 300,
  },
  //是否允许点击遮罩关闭
  overlayClick: {
    type: Boolean,
    default: true,
  },
  transprent: {
    type: [Boolean, String],
    default: false,
  },
  //如果显示关闭。标题栏被替换为左标题右关闭按钮。
  closeable: {
    type: [Boolean, String],
    default: false,
  },
  color: {
    type: String,
    default: 'white',
  },
  title: [String],
  okText: {
    type: [String],
    default: '完成',
  },
  okColor: {
    type: [String],
    default: 'primary',
  },
  //true时，确认按钮将出现加载状态。
  okLoading: {
    type: [Boolean, String],
    default: false,
  },
  cancelText: {
    type: [String],
    default: '取消',
  },
  hideCancel: {
    type: [Boolean, String],
    default: false,
  },
  //隐藏工具栏，标题，取消，确认
  hideHeader: {
    type: [Boolean, String],
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  zIndex: {
    type: [Number, String],
    default: 401,
  },
  unit: {
    type: String,
    default: 'rpx',
  },
  disabbleScroll: {
    type: Boolean,
    default: false,
  },
  /** 是否嵌入弹层，开启后将在它的父组件内执行弹层。 */
  inContent: {
    type: Boolean,
    default: false,
  },
  footHeight: {
    type: Number,
    default: 0,
  },
  /** 是否使用teleport */
  teleport: {
    type: Boolean,
    default: true,
  },
  /**打开前执行 */
  beforeOpen: {
    type: Function,
    default: null,
  },
  /**关点击ok前执行，如果返回是false，将阻止关闭. */
  beforeOk: {
    type: Function,
    default: null,
  },
  /**点击取消前执行，如果返回fase将阻止关闭. */
  beforeCance: {
    type: Function,
    default: null,
  },
})
const emits = defineEmits(['click', 'open', 'close', 'update:show', 'ok', 'cancel'])
const proxy = getCurrentInstance()?.proxy ?? null
const sysinfo = useWindowInfo()
// 设置响应式全局组件库配置表。
const tmcfg = computed<tmVuetify>(() => store.tmStore)
//自定义样式：
const customCSSStyle = computed(() => computedStyle(props))
//自定类
const customClass = computed(() => computedClass(props))
//是否暗黑模式。
const isDark = computed(() => computedDark(props, tmcfg.value))
//计算主题
const tmcomputed = computed<cssstyle>(() => computedTheme(props, isDark.value, tmcfg.value))

const reverse = ref(true)
const timeid = ref(0)
let timerId: any = NaN
let timerIdth: any = NaN
let timerIdth_flas = false
uni.hideKeyboard()
let _show = ref(props.show)
const isPlaying = ref(false)
function debounce(func: Function, wait = 500, immediate = false) {
  // 清除定时器
  if (!isNaN(timerId)) clearTimeout(timerId)
  // 立即执行，此类情况一般用不到

  if (immediate) {
    timerId = setTimeout(() => {
      timerId = NaN
    }, wait)
    typeof func === 'function' && func()
  } else {
    // 设置定时器，当最后一次操作后，timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
    timerId = setTimeout(() => {
      typeof func === 'function' && func()
    }, wait)
  }
}

function throttle(func: Function, wait = 500, immediate = true) {
  if (immediate) {
    if (!timerIdth_flas) {
      timerIdth_flas = true
      // 如果是立即执行，则在wait毫秒内开始时执行
      typeof func === 'function' && func()

      timerIdth = setTimeout(() => {
        timerIdth_flas = false
      }, wait)
    }
  } else {
    if (!timerIdth_flas) {
      timerIdth_flas = true
      // 如果是非立即执行，则在wait毫秒内的结束处执行
      timerIdth = setTimeout(() => {
        timerIdth_flas = false
        typeof func === 'function' && func()
      }, wait)
    }
  }
}

timeid.value = uni.$tm.u.getUid(4)
if (_show.value) {
  reverse.value = false
}
watch(
  () => props.show,
  (val) => {
    if (val) {
      reverse.value = true
    } else {
      reverse.value = false
      overlayAni.value?.close()
    }
    if (_show.value !== props.show) {
      nextTick(() => {
        drawerANI.value?.play()
        isPlaying.value = true
      })
    }
    _show.value = props.show
  }
)
onMounted(() => {
  if (_show.value) {
    open()
  }
})
const ok_loading = computed(() => props.okLoading)
const loading = ref(false)
const round_rp = computed(() => {
  if (aniname.value == 'left') return 'round-r-' + props.round
  if (aniname.value == 'right') return 'round-l-' + props.round
  if (aniname.value == 'up') return 'round-b-' + props.round
  if (aniname.value == 'down') return 'round-t-' + props.round
  if (aniname.value == 'zoom') return 'round-' + props.round
})
const reverse_rp = computed(() => {
  if (aniname.value != 'zoom') return reverse.value
  return !reverse.value
})
const aniname = computed(() => {
  if (props.placement == 'center') return 'zoom'
  if (props.placement == 'top') return 'up'
  if (props.placement == 'bottom') return 'down'
  return props.placement
})
const anwidth = computed(() => {
  if (aniname.value == 'zoom') {
    return props.width + props.unit
  }
  if (props.placement == 'left' || props.placement == 'right') {
    return props.width + props.unit
  }
  return sysinfo.width + 'px'
})
const anheight = computed(() => {
  let wucha = 0
  if (props.placement == 'top' || props.placement == 'bottom' || aniname.value == 'zoom') {
    return props.height + wucha + props.unit
  }
  return sysinfo.height + 'px'
})
const contentHeight = computed(() => {
  let base_height = props.hideHeader ? 0 : 44
  let _footerHeight = uni.$tm.u.topx(props.footHeight)
  if (props.placement == 'top' || props.placement == 'bottom' || aniname.value == 'zoom') {
    let h = props.height
    if (props.unit == 'rpx') {
      h = uni.upx2px(props.height)
    }
    return h - base_height - _footerHeight + 'px'
  }
  return sysinfo.height - base_height - _footerHeight + 'px'
})
const align_rp = computed(() => {
  if (aniname.value == 'down') {
    return 'flex-col-bottom-center'
  }
  if (aniname.value == 'up') {
    return 'flex-top-custom'
  }
  if (aniname.value == 'left') {
    return 'flex-row-top-start'
  }
  if (aniname.value == 'right') {
    return 'flex-row-bottom-start'
  }
  if (aniname.value == 'zoom') {
    return 'flex-center'
  }
})

async function _beforeOpenFun() {
  if (typeof props.beforeOpen === 'function') {
    loading.value = true
    let p = await props.beforeOpen()
    if (typeof p === 'function') {
      p = await p()
    }
    loading.value = false
  }
}

async function _beforeOkFun() {
  let p = true
  if (typeof props.beforeOk === 'function') {
    loading.value = true
    p = await props.beforeOk()
    if (typeof p === 'function') {
      p = await p()
    }
    loading.value = false
    if (!p) return
  }
  return p
}

async function _beforeCancelFun() {
  let p = true
  if (typeof props.beforeCance === 'function') {
    loading.value = true
    p = await props.beforeCance()
    if (typeof p === 'function') {
      p = await p()
    }
    loading.value = false
    if (!p) return
  }
  return p
}

function OverLayOpen() {
  // nextTick(function() {
  // 	drawerANI.value?.play();
  // })
  _beforeOpenFun()
  _show.value = true
  emits('open')
  emits('update:show', true)
}
function overclose() {
  nextTick(() => {
    _show.value = false
    emits('close')
    emits('update:show', false)
  })
}
async function overlayClickFun(e: Event) {
  if (!props.overlayClick || props.disabled || !overlayAni.value || loading.value || isPlaying.value) return
  emits('click', e)
  if (!(await _beforeCancelFun())) return
  reverse.value = false
  throttle(
    async () => {
      emits('cancel')
      overlayAni.value?.close()
      drawerANI.value?.play()
    },
    props.duration + 80,
    true
  )
}
function playEndEvent() {
  isPlaying.value = false
}

async function ok() {
  if (props.disabled || loading.value) return
  if (!(await _beforeOkFun())) return
  reverse.value = false
  debounce(
    () => {
      emits('ok')
      overlayAni.value?.close()
      drawerANI.value?.play()
    },
    500,
    true
  )
}

async function cancel() {
  if (props.disabled || loading.value) return
  if (!(await _beforeCancelFun())) return
  reverse.value = false
  debounce(
    () => {
      emits('cancel')
      overlayAni.value?.close()
      drawerANI.value?.play()
    },
    500,
    true
  )
}

//外部调用。
function open() {
  reverse.value = true
  _show.value = true
  nextTick(() => {
    drawerANI.value?.play()
  })
}

//外部手动调用关闭方法
function close() {
  reverse.value = false
  overlayAni.value?.close()
  drawerANI.value?.play()
}

//外部调用的方法。
defineExpose({
  close: close,
  open: open,
})
</script>

<style scoped>
.flex-left-custom {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
}

.flex-right-custom {
  display: flex;
  justify-content: flex-start;
  align-items: flex-end;
}

.flex-top-custom {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
}

.flex-end-custom {
  display: flex;
  justify-content: flex-end;
  align-items: flex-end;
}

.flex-center-custom {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row;
}
</style>
